-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local class = require "mc.class"
local log = require 'mc.logging'
local prop_def = require "macros.property_def"
local bios_factory = require 'factory.bios_factory'
local prop_method_app = require 'macros.property_method_app'
local bs_util = require 'util.base_util'
local json_util = require 'util.json_util'
local setting = require 'pojo.config.file.setting'
local setting_service = require 'service.setting_service'
local registry_service = require 'service.registry_service'
local chk_strategy = require "util.check.check_strategy"
local cfg_handler = require 'handler.config_handler'
local json = require "cjson"
local prop_global = require 'macros.property_global'
local error_engineer = require 'handler.error_engineer'
local base_messages = require 'messages.base'
local custom_messages = require 'messages.custom'
local initiator = require 'mc.initiator'
local policy_config_transfer = require 'domain.policy_config.policy_config_transfer'
local skynet = require 'skynet'
local msg = require 'bios.ipmi.ipmi_message'
local s_pack = string.pack
local s_unpack = string.unpack
local context = require 'mc.context'
local file_service = class()

function file_service.get_file_path(file_prop, sys_id)
    local bios_service = bios_factory.get_service('bios_service')
    if not bios_service then
        log:error("get_file_obj: bios_service is nil!")
        return nil
    end

    local file_path = bios_service:get_bios_prop(file_prop, sys_id)
    if not file_path then
        log:error("get_file_obj: sys_id %s get %s fail!", sys_id, file_prop)
        return nil
    end

    return file_path
end

-- 将文件转化为json格式
function file_service.get_file_json(file_prop, sys_id)
    local file_path = file_service.get_file_path(file_prop, sys_id)
    if not file_path then
        log:error("get_file_obj: get %s path fail!", file_prop)
        return bs_util.err_msg(prop_def.RESPONSE_FILE_PATH_NULL, prop_def.RESPONSE_INNER_ERROR)
    end

    local file_json = bs_util.get_file_json(file_path)
    if not file_json then
        log:error("get_file_obj: get %s json fail!", file_path)
        return bs_util.err_msg(prop_def.RESPONSE_FILE_JSON_NULL, prop_def.RESPONSE_INNER_ERROR)
    end

    return bs_util.err_msg(prop_def.E_OK, file_json)
end

-- 读取配置文件
function file_service.construct_register_service(sys_id)
    local message = file_service.get_file_json(prop_method_app.BIOS_FILE_REGISTRY_NAME, sys_id)
    if message.ErrCode ~= prop_def.E_OK then
        log:error("construct_register_service: get registry_json fail!")
        return message
    end

    local registry_json = message.Data
    local valid = registry_service.judge_registry_json_valid(registry_json)
    if not valid then
        log:error("construct_register_service: registry_json invalid!")
        return bs_util.err_msg(prop_def.RESPONSE_REGISTRY_JSON_INVALID,
            prop_def.RESPONSE_INNER_ERROR)
    end

    local registry_ser = registry_service.new(registry_json)
    if not registry_ser then
        log:error("construct_register_service: get registry_ser fail!")
        return bs_util.err_msg(prop_def.RESPONSE_REGISTRY_SERVICE_NULL,
            prop_def.RESPONSE_INNER_ERROR)
    end

    return bs_util.err_msg(prop_def.E_OK, registry_ser)
end

function file_service.construct_setting_service(file_prop, sys_id)
    local message = file_service.get_file_json(file_prop, sys_id)
    if message.ErrCode ~= prop_def.E_OK then
        log:error("construct_setting_service: get %s json fail!", file_prop)
        return message
    end

    local setting_json = message.Data
    local setting_ser = setting_service.new(setting_json)
    if not setting_ser then
        log:error("construct_setting_service: get setting_obj(%s) fail!", file_prop)
        return bs_util.err_msg(prop_def.RESPONSE_CURRENTVALUE_SERVICE_NULL,
            prop_def.RESPONSE_INNER_ERROR)
    end

    return bs_util.err_msg(prop_def.E_OK, setting_ser)
end

-- 处理currentvalue.json、registry.json
function file_service:contruct_common_service(sys_id)
    local message = file_service.construct_register_service(sys_id)
    if message.ErrCode ~= prop_def.E_OK then
        log:error("contruct_common_service: get registry service fail!")
        return message
    end
    self.register_service = message.Data

    message = file_service.construct_setting_service(prop_method_app.BIOS_FILE_CURRENT_VALUE_NAME, sys_id)
    if message.ErrCode == prop_def.RESPONSE_FILE_JSON_NULL then
        return bs_util.err_msg(prop_def.RESPONSE_CURRENTVALUE_JSON_NULL,
            prop_def.RESPONSE_INNER_ERROR)
    elseif message.ErrCode ~= prop_def.E_OK then
        log:error("construct_setting_service: get current service fail!")
        return message
    end
    self.current_service = message.Data

    return bs_util.err_msg(prop_def.E_OK, nil)
end

-- 处理setting.json、用户配置的xml
function file_service:contruct_setting_service(xml_json, sys_id)
    local message = file_service.construct_setting_service(prop_method_app.BIOS_FILE_SETTING_NAME, sys_id)
    if message.ErrCode == prop_def.E_OK then
        self.setting_service = message.Data
    end

    local user_setting_ser = setting_service.new(xml_json)
    if not user_setting_ser then
        return bs_util.err_msg(prop_def.RESPONSE_CURRENTVALUE_SERVICE_NULL,
            prop_def.RESPONSE_INNER_ERROR)
    end
    self.user_setting_service = user_setting_ser

    return bs_util.err_msg(prop_def.E_OK, nil)
end

function file_service:check_attributes_valid(check_data, key, export_flag)
    local current_ser = self.current_service
    local prop_value = check_data.attribute:get_val_by_name(key)

    if prop_value == nil or type(prop_value) ~= prop_def.BOOLEAN_TYPE then
        log:error("check_attributes_valid: %s get value fail or tyep(%s) is not bool.", key, type(prop_value))
        return prop_def.E_FAILURE
    end

    -- 如果属性不可更改
    -- 导出配置,则此属性无需导出
    -- 导入配置,判断导入属性的值是否与当前配置相同,相同则可以配置,不同则配置失败
    if prop_value == true then
        if export_flag then
            -- 无需导出该属性
            return prop_def.E_FAILURE
        else
            -- redfish导入非法依赖关系的配置，需要报错
            if self.redfish_import then
                return prop_def.E_FAILURE
            end
            local valid = current_ser:compare_setting(check_data.attr_name, check_data.attr_value)
            if valid ~= prop_def.E_OK then
                return prop_def.E_FAILURE
            end
        end
    end
end

-- 检查属性的是否不可更改、是否隐藏、是否只读的状态是否合法
function file_service:check_attributes_status(attribute, attr_name, attr_value, export_flag)
    local bios_attribute_prop_array = {}
    local attribute_prop_list = {prop_def.REGRIST_PROP_IMMUTABLE,
        prop_def.REGRIST_PROP_HIDDEN, prop_def.REGRIST_PROP_READONLY}
    bios_attribute_prop_array[prop_def.REGRIST_PROP_IMMUTABLE] = prop_def.REGRIST_TYPE_PROP_IMMUTABLE
    bios_attribute_prop_array[prop_def.REGRIST_PROP_HIDDEN] = prop_def.REGRIST_TYPE_PROP_HIDE
    bios_attribute_prop_array[prop_def.REGRIST_PROP_READONLY] = prop_def.REGRIST_TYPE_PROP_READONLY

    for i = 1, #attribute_prop_list do
        local key = attribute_prop_list[i]
        local value = bios_attribute_prop_array[key]
        local check_data = {}
        check_data.attribute = attribute
        check_data.attr_name = attr_name
        check_data.attr_value = attr_value

        local valid = self:check_attributes_valid(check_data, key, export_flag)
        if valid == prop_def.E_FAILURE then
            error_engineer.add_status_error(key, attr_name)
            return value
        end
    end

    -- 此属性可以导出
    return prop_def.E_OK
end

-- 检查属性合法性
function file_service:check_cfg(attr_name, attr_value, export_flag)
    local registry_ser = self.register_service
    if attr_name == nil or attr_value == nil then
        log:error("check_cfg: parameter error," .. attr_name)
        return prop_def.E_FAILURE
    end

    -- 根据Attribute的属性值判读配置项attr_name配置的attr_value属性值是否合法
    local attribute = registry_ser:get_attribute_by_name(attr_name)
    if attribute == nil then
        log:error('check_cfg: %s has not attribute.', attr_name)
        return prop_def.E_FAILURE
    end

    -- 校验配置的值是否可更改
    local check_res = self:check_attributes_status(attribute, attr_name,
        attr_value, export_flag)
    if check_res ~= prop_def.E_OK then
        -- 此属性校验失败,可能无需导出
        log:info("check_cfg: check_attributes_status %s fail.", attr_name)
        return prop_def.E_FAILURE
    end

    local prop_type = attribute:get_val_by_name(prop_def.REGRIST_PROP_TYPE)
    if prop_type == nil then
        log:error("check_cfg: get %s type fail.", attr_name)
        return prop_def.E_FAILURE
    end

    local strategy = chk_strategy.get_instance()
    if strategy == nil then
        log:error("check_cfg: get strategy fail.")
        return prop_def.E_FAILURE
    end

    -- 校验配置的值是否在配置列表中
    local is_valid = strategy:is_config_valid(prop_type, attribute, attr_value)
    -- 校验通过,更新attibute的currentValue,为了后续循环校验使用
    if is_valid == prop_def.E_OK then
        attribute:set_val_by_name(prop_def.REGRIST_PROP_CURRENTVALUE, attr_value)
    end

    return is_valid
end

function file_service:can_export_or_import(key, value, cfg_data, flag_data)
    local registry_ser = self.register_service
    local res_cfg
    local valid = prop_def.E_FAILURE

    local export_flag = flag_data.export_flag
    local delete_err_flag = flag_data.delete_err_flag
    local traverse_cfg = cfg_data.traverse_cfg
    local handler = cfg_data.handler
    local output_cfg = cfg_data.output_cfg

    local is_valid = self:check_cfg(key, value, export_flag)
    -- 剩下的都是校验不通过的属性
    if is_valid ~= prop_def.E_OK then
        -- 此时都是校验不通过的属性,都不需要导出
        if delete_err_flag then
            res_cfg = handler.handle(traverse_cfg, output_cfg)
            self.traverse_cfg = traverse_cfg
            valid = prop_def.E_END
        end
    -- 校验通过,说明还需要进行一轮校验,并且还需要更新依赖关系
    else
        registry_ser:update_attribute_dependency(key, prop_def.REGRIST_PROP_CURRENTVALUE, true)
        traverse_cfg[key] = nil
        valid = prop_def.E_CONTINUE
    end

    return valid, res_cfg
end

function file_service:find_bios_cfgs(cfg_data, flag_data)
    local res = {}
    local res_cfg

    local is_continue_check = false
    local traverse_list = cfg_data.traverse_list
    local traverse_cfg = cfg_data.traverse_cfg

    local list_len = #traverse_list
    for i = 1, list_len, 1 do
        local key = traverse_list[i]
        local value = traverse_cfg[key]
        if key ~= nil and value ~= nil then
            local valid
            valid, res_cfg = self:can_export_or_import(key, value, cfg_data, flag_data)
            if valid == prop_def.E_END then
                break
            end

            if valid == prop_def.E_CONTINUE then
                is_continue_check = true
            end
        end
    end

    res.is_continue_check = is_continue_check
    res.res_cfg = res_cfg

    return res
end

-- 校验属性,handler有两种:导入、导出
--       功能         返回值
-- 导入: 校验属性     res_cfg(导入失败的属性)
-- 导出: 校验属性     res_cfg(能够导出的属性)
function file_service:check_and_get_attributes(setting_obj, handler, export_flag)
    local registry_ser = self.register_service
    local traverse_list = self.traverse_list_data
    if not registry_ser or not setting_obj or not traverse_list then
        log:error("check_and_get_attributes: paramer is invalid.")
        return bs_util.err_msg(prop_def.RESPONSE_REGISTRY_NULL,
            prop_def.RESPONSE_IMPORT_INNER_ERROR)
    end

    -- 根据依赖关系决定哪些属性可以导出
    -- 如果都校验不通过则结束
    -- 只要有一个属性校验通过,就需要重新更新依赖关系，并重新校验
    local is_continue_check = true
    local traverse_cfg_obj = setting.new(setting_obj:get_cfgs())
    local output_cfg_obj = setting.new(setting_obj:get_cfgs())
    local delete_err_flag = false
    local res_data = {}

    while is_continue_check do
        -- 遍历配置项,进行校验
        is_continue_check = false
        -- 用于遍历
        local traverse_cfg = traverse_cfg_obj:get_cfgs()
        -- 用于最终导出的配置列表
        local output_cfg = output_cfg_obj:get_cfgs()
        local cfg_data = {}
        cfg_data.traverse_list = traverse_list
        cfg_data.traverse_cfg = traverse_cfg
        cfg_data.output_cfg = output_cfg
        cfg_data.handler = handler

        local flag_data = {}
        flag_data.is_continue_check = is_continue_check
        flag_data.export_flag = export_flag
        flag_data.delete_err_flag = delete_err_flag

        res_data = self:find_bios_cfgs(cfg_data, flag_data)
        is_continue_check = res_data.is_continue_check

        -- 此时剩下的都是校验不通过的属性,还需要进行循环一次
        -- 将这些校验不通过的属性删除,保留校验通过的属性
        if not is_continue_check and not delete_err_flag then
            is_continue_check = true
            delete_err_flag = true
        end
    end

    return bs_util.err_msg(prop_def.E_OK, res_data.res_cfg)
end

function file_service:export_bios_json(sys_id)
    local message = self:contruct_common_service(sys_id)
    if message.ErrCode == prop_def.RESPONSE_CURRENTVALUE_JSON_NULL then
        log:error("export_bios_json: current file is empty")
        return bs_util.err_msg(prop_def.E_OK, {})
    end
    if message.ErrCode ~= prop_def.E_OK then
        log:error("export_bios_json: contruct_common_service fail!")
        return message
    end

    local registry_ser = self.register_service
    local current_ser = self.current_service
    local current_obj = current_ser:get_setting_obj()
    -- 使用currentvalue更新registry对象
    message = registry_ser:update_registry(current_obj)
    if message.ErrCode ~= prop_def.E_OK then
        log:error("export_bios_json: update registry fail!")
        return message
    end

    -- 将json数据变为有序
    local traverse_list = json_util.json_to_list(current_obj:get_cfgs(),
        registry_ser:get_traverse_list())
    if not traverse_list then
        log:error("export_bios_json: json_to_list fail!")
        return bs_util.err_msg(prop_def.RESPONSE_JSON_TO_LIST_FAIL, {})
    end

    self.traverse_list_data = traverse_list
    -- 校验和导出属性
    message = self:check_and_get_attributes(current_obj,
        cfg_handler.export_handler, true)
    if message.ErrCode ~= prop_def.E_OK then
        log:error("export_bios_json: check and get attributes fail!")
        return message
    end
    return message
end

-- 对外接口:配置导出
function file_service:export_bios_config(file_type, system_id)
    local bios_ser = bios_factory.get_service('bios_service')
    if not bios_ser then
        log:error('[bios]export bios config: get bios service fail')
        error(base_messages.InternalError())
    end
    return bios_ser:get_config_data(file_type, system_id)
end

function file_service:check_setting_cfg()
    local setting_obj = self.user_setting_service:get_setting_obj()
    -- 校验导入的属性
    local message = self:check_and_get_attributes(setting_obj,
        cfg_handler.import_handler, false)
    if message.ErrCode ~= prop_def.E_OK then
        return message
    end

    if self.from_web then
        -- web存在导入失败的属性，需要隐藏，不能报失败
        return bs_util.err_msg(prop_def.E_OK, prop_def.RESPONSE_IMPORT_OK)
    end
    local output_cfg = message.Data
    -- 存在导入失败的属性
    if output_cfg then
        log:error('check_setting_cfg: import setting(%s) fail.', output_cfg)
        return bs_util.err_msg(prop_def.RESPONSE_IMPORT_ERROR,
            output_cfg)
    end

    return bs_util.err_msg(prop_def.E_OK, prop_def.RESPONSE_IMPORT_OK)
end

-- 1、根据currentvalue.json、setting.json更新registry
-- 2、检查配置合法性
function file_service:update_registry_and_check_cfg(is_check_setting)
    local registry_ser = self.register_service
    local user_setting_ser = self.user_setting_service
    local ok, _ = pcall(function()
        user_setting_ser:check_boot_order()
    end)
    if not ok then
        log:error('update_registry_and_check_cfg: check boot order error.')
        error_engineer.set_error(prop_def.RESPONSE_BOOTTYPEORDER_ERROR)
        if not self.from_web then
            return bs_util.err_msg(prop_def.RESPONSE_BOOTTYPEORDER_ERROR,
                prop_def.REGRIST_PROP_BOOTTYPEORDER)
        end
        table.insert(self.invalid_cfg, prop_def.REGRIST_PROP_BOOTTYPEORDER0)
        table.insert(self.invalid_cfg, prop_def.REGRIST_PROP_BOOTTYPEORDER1)
        table.insert(self.invalid_cfg, prop_def.REGRIST_PROP_BOOTTYPEORDER2)
        table.insert(self.invalid_cfg, prop_def.REGRIST_PROP_BOOTTYPEORDER3)
    end

    -- 根据currentvalue.json更新registry对象
    local current_ser = self.current_service
    local message = registry_ser:update_registry(current_ser:get_setting_obj())
    if message.ErrCode ~= prop_def.E_OK then
        log:error("update_registry_and_check_cfg: update current registry fail!")
        return message
    end

    local setting_ser = self.setting_service
    if is_check_setting and setting_ser then
        -- 根据setting.json更新registry对象
        message = registry_ser:update_registry(setting_ser:get_setting_obj())
        if message.ErrCode ~= prop_def.E_OK then
            log:error("update_registry_and_check_cfg: update setting registry fail!")
            return message
        end
    end

    -- 检查配置是否合法
    message = self:check_setting_cfg()
    return message
end

local function set_file_ineffective(sys_id)
    pcall(function()
        local bios_ser = bios_factory.get_service('bios_service')
        bios_ser:set_ineffective(sys_id)
        bios_ser:set_file_change(prop_def.BIOS_SETTING_FILE_CHANGED, 0, 0, sys_id)
    end)
end

-- web导入，需要去掉非法配置
function file_service:remove_invalid_web_config(from_web, config_data)
    for _, fill_key in pairs(self.fill_cfg) do
        config_data[prop_def.REGRIST_PROP_ATTRIBUTES][fill_key] = self.current_service:get_prop_val(fill_key)
    end
    if not from_web or not config_data then
        return
    end
    for _, invalid_key in pairs(self.invalid_cfg) do
        config_data[prop_def.REGRIST_PROP_ATTRIBUTES][invalid_key] = nil
    end
    log:notice('[bios]remove some invalid web config')
end

local function update_denpendency(sys_id)
    skynet.fork(function()
        skynet.sleep(100)
        local bios_ser = bios_factory.get_service('bios_service')
        if not bios_ser then
            return
        end
        local ok, res = pcall(function()
            bios_ser:update_denpendency(sys_id)
        end)
        if ok then
            log:notice('[bios]update denpendency success after import')
        else
            log:error('[bios]update denpendency fail after import %s', res)
        end
    end)
end

function file_service:check_and_fill_boot_order()
    self.fill_cfg = {}
    local boot_order = {
        [prop_def.REGRIST_PROP_BOOTTYPEORDER0] = self.json_data[prop_def.REGRIST_PROP_BOOTTYPEORDER0] or false,
        [prop_def.REGRIST_PROP_BOOTTYPEORDER1] = self.json_data[prop_def.REGRIST_PROP_BOOTTYPEORDER1] or false,
        [prop_def.REGRIST_PROP_BOOTTYPEORDER2] = self.json_data[prop_def.REGRIST_PROP_BOOTTYPEORDER2] or false,
        [prop_def.REGRIST_PROP_BOOTTYPEORDER3] = self.json_data[prop_def.REGRIST_PROP_BOOTTYPEORDER3] or false
    }
    local need_fill = false
    for _, boot_order_value in pairs(boot_order) do
        if boot_order_value then
            need_fill = true
        end
    end
    if not need_fill then
        return
    end
    for boot_order_key, boot_order_value in pairs(boot_order) do
        if not boot_order_value then
            self.json_data[boot_order_key] = self.current_service:get_prop_val(boot_order_key)
            table.insert(self.fill_cfg, boot_order_key)
        end
    end
end

-- 配置写入setting.json文件
function file_service:set_json_cfg(is_check_setting, is_append, sys_id)
    self.invalid_cfg = {}
    self:check_and_fill_boot_order()
    local json_data = self.json_data
    local message = self:contruct_setting_service(json_data, sys_id)
    if message.ErrCode ~= prop_def.E_OK then
        log:error("set_json_cfg: contruct_setting_service fail!")
        return message
    end
    self.traverse_cfg = nil
    -- 检查配置是否合法
    message = self:update_registry_and_check_cfg(is_check_setting)
    -- 配置不是来自web、并且配置非法
    if not self.from_web and message.ErrCode ~= prop_def.E_OK then
        log:error("set_json_cfg: update_registry_and_check_cfg fail!")
        return message
    end

    -- 配置合法,将配置写入setting.json文件
    local file_prop = prop_method_app.BIOS_FILE_SETTING_NAME
    local file_path = file_service.get_file_path(file_prop, sys_id)
    if not file_path then
        log:error("set_json_cfg: get %s path fail!", file_prop)
        return bs_util.err_msg(prop_def.RESPONSE_FILE_PATH_NULL, prop_def.RESPONSE_INNER_ERROR)
    end

    local order_json_data = json.json_object_ordered_decode(self.raw_content)
    self:remove_invalid_web_config(self.from_web, order_json_data)
    self.traverse_cfg = nil
    local err_code = json_util.write_file_extend(file_path,
        order_json_data[prop_def.REGRIST_PROP_ATTRIBUTES], is_append)
    if err_code == prop_def.E_FAILURE then
        log:error('set_json_cfg: write_file_data fail.')
        return bs_util.err_msg(err_code, prop_def.RESPONSE_INNER_ERROR)
    end

    prop_global.G_BIOS_SETTING_FILE_STATE = prop_def.BIOS_SETTING_FILE_INEFFECTIVE
    set_file_ineffective(sys_id)
    update_denpendency(sys_id)
    local ok, res = pcall(function()
        local boot_ser = bios_factory.get_service('boot_service')
        local boot_obj = boot_ser:get_obj(sys_id)
        if boot_obj then
            boot_obj:update_order_from_file('setting.json')
        end
    end)
    if not ok then
        log:error('update order from setting failed, res: %s', res)
    end
    return bs_util.err_msg(prop_def.E_OK, prop_def.RESPONSE_IMPORT_OK)
end

function file_service:check_prop_valid(setup_json)
    local registry_ser = self.register_service
    if not setup_json or not registry_ser then
        log:error("import bios json: check prop valid fail.")
        error(base_messages.MalformedJSON())
    end

    local setup_list = json_util.get_list(setup_json)
    if setup_list == nil then
        log:error("import bios json: json is nil, import fail!")
        error(base_messages.MalformedJSON())
    end

    local len = #setup_list
    for i = 1, len do
        local attribute_name = setup_list[i]
        local attribute = registry_ser:get_attribute_by_name(attribute_name)
        if attribute == nil then
            if self.redfish_import then
                error(base_messages.PropertyUnknown('Attributes/' .. attribute_name))
            else
                log:notice('[bios]import web config, filter attribute(%s)', attribute_name)
                setup_json[attribute_name] = nil
            end
        end
    end
    return
end

local function is_file_changed(sys_id)
    local bios_ser = bios_factory.get_service('bios_service')
    return bios_ser:get_prop('FileChangeFlag', sys_id) == prop_def.BIOS_SETTING_FILE_CHANGED
end

function file_service:import_setup_json(ctx, file_type, content, sys_id, from_web)
    if not file_type or not content then
        log:error("import bios json: invalid param.")
        error(base_messages.InternalError())
    end

    if file_type ~= 'Setting' then
        log:error("import bios json: file %s not support write.", file_type)
        error(base_messages.InternalError())
    end

    local message = self:contruct_common_service(sys_id)
    if message.ErrCode ~= prop_def.E_OK then
        log:error("import bios json: contruct common service fail!")
        error(base_messages.MalformedJSON())
    end

    local ok, user_json = pcall(function()
        return json.decode(content)
    end)
    if not ok then
        log:error("import bios json: json format error!")
        error(base_messages.MalformedJSON())
    end

    self.from_web = from_web
    self.raw_content = content
    local setup_json = user_json[prop_def.REGRIST_PROP_ATTRIBUTES]
    self:check_prop_valid(setup_json)

    -- 将json数据变为有序
    local traverse_list = json_util.json_to_list(setup_json,
        self.register_service:get_traverse_list())
    if setup_json == nil or traverse_list == nil then
        log:error("import bios json: json is nil, import fail!")
        error(base_messages.MalformedJSON())
    end
    self.json_data = setup_json
    self.traverse_list_data = traverse_list

    error_engineer.init(self.register_service)
    local file_changed = is_file_changed(sys_id)
    message = self:set_json_cfg(file_changed, file_changed, sys_id)
    if message.ErrCode ~= prop_def.E_OK then
        error_engineer.throws_exception()
    end

    return prop_def.HTTP_OK
end


function file_service:import_bios_json(ctx, file_type, content, sys_id)
    local ok, res = pcall(function()
        self.redfish_import = false
        return self:import_setup_json(ctx, file_type, content, sys_id)
    end)
    self:destory_after_import()
    if not ok then
        bs_util.record_operation(ctx, sys_id, "Import BIOS configuration failed")
        error(res)
    end
    bs_util.record_operation(ctx, sys_id, "Import BIOS configuration successfully")
    return res
end

function file_service:import_redfish_json(ctx, file_type, content, sys_id)
    local ok, res = pcall(function()
        self.redfish_import = true
        return self:import_setup_json(ctx, file_type, content, sys_id)
    end)
    self:destory_after_import()
    if not ok then
        log:error("[bios]system%s Failed to issue BIOS Setup configuration: %s", sys_id, res)
        bs_util.record_operation(ctx, sys_id, "Failed to issue BIOS Setup configuration")
        error(res)
    end
    bs_util.record_operation(ctx, sys_id,
        "BIOS Setup configuration issued successfully and will take effect upon the next startup")
    return res
end

function file_service:destory_after_import()
    skynet.fork(function()
        if not self.register_service then
            return
        end
        self.register_service:destroy()
        self.register_service = nil
        log:notice('[bios]destory after import success')
    end)
end

function file_service:import_web_json(ctx, file_type, content, sys_id)
    local ok, res = pcall(function()
        self.redfish_import = true
        return self:import_setup_json(ctx, 'Setting', content, sys_id, true)
    end)
    self:destory_after_import()
    if not ok then
        log:error('[bios]import web json fail, err is %s', res)
        bs_util.record_operation(ctx, sys_id, "Failed to issue BIOS Web Setup configuration")
        error(res)
    end
    bs_util.record_operation(ctx, sys_id,
        "BIOS Web Setup configuration issued successfully and will take effect upon the next startup")
    return res
end

local START_FINISH<const> = 254
function file_service:import_policy_config(ctx, file_type, content, sys_id)
    local bios_ser = bios_factory.get_service('bios_service')
    if not bios_ser or bios_ser:get_prop('SystemStartupState', sys_id) ~= START_FINISH then
        log:error('[bios]import policy config: curent state not support')
        bs_util.record_operation(ctx, sys_id, "Failed to issue BIOS Setup configuration")
        error(custom_messages.BiosStateNotAllowed())
    end
    local policy_transfer = policy_config_transfer.get_instance()
    local ok, err = pcall(function()
        policy_transfer:send(content, sys_id)
    end)
    if not ok then
        log:error('[bios]import policy config fail: %s', err.Code or err)
        bs_util.record_operation(ctx, sys_id, "Failed to issue BIOS Setup configuration")
        error(err.Code or base_messages.InternalError())
    else
        bs_util.record_operation(ctx, sys_id, "Issue BIOS Setup Configuration successfully")
        return prop_def.HTTP_OK
    end
end

function file_service:import_json(ctx, file_type, content, sys_id)
    local type_tbl = {
        PolicyConfig = file_service.import_policy_config,
        Setting = file_service.import_redfish_json,
        WebConfig = file_service.import_web_json
    }
    local import_fun = type_tbl[file_type]
    if not import_fun then
        log:error('[bios]import bios config: type(%s) invalid', file_type)
        error(base_messages.InternalError())
    end
    return import_fun(self, ctx, file_type, content, sys_id)
end

local function construct_result_obj(result_json)
    local obj = {}
    local msg_json = result_json['Messages']
    if not msg_json then
        return obj
    end
    for _, v in pairs(msg_json) do
        local related_prop = v['RelatedProperties']
        if related_prop then
            for _, prop_str in pairs(related_prop) do
                obj[string.match(prop_str, "/(.+)")] = 1
            end
        end
    end
    return obj
end

function file_service.write_setting_log(result_json, setting_json, cur_json)
    if not result_json or not setting_json or not cur_json then
        log:error('[bios]write setting log fail:json is nil')
        return false
    end
    local result_obj = construct_result_obj(result_json)
    local context = initiator.new('IPMI', 'N/A', 'HOST')
    for prop, val in pairs(setting_json) do
        if not result_obj[prop] then
            local cur_val = cur_json[prop] or ''
            log:operation(context, 'BIOS', 'Set %s from [%s] to [%s] success,EvtCode:%s',
                prop, cur_val, val, '21700BE0')
        end
    end
    log:notice('[bios] operator result file success')
end

local BIOS_CONFIG_PXE_TIMEOUT_RETRY<const> = "PxeTimeoutRetryControl"
function file_service:set_bios_config_item(req, ctx, file_type)
    local manu_id = self:get_manu_id()
    local data = file_service:validate_config_item(req, manu_id);
    local sys_id = bs_util.get_system_id(ctx)
    if data then
        local message = "Set BIOS Setup configuration failed"
        file_service:record_operation(file_service:get_op_initiator(), sys_id, message)
        return data
    end
    local setup_data = {}
    local option_value = prop_def.OPTION_CODE[req.Option]
    if not option_value then
        log:error("[bios] incorrect configuration option")
        return msg.SetBiosConfigItemRsp.new(prop_def.COMP_CODE_OUTOF_RANGE, manu_id, '')
    end
    local time_out_value = {}
    time_out_value[BIOS_CONFIG_PXE_TIMEOUT_RETRY] = option_value
    setup_data[prop_def.REGRIST_PROP_ATTRIBUTES] = time_out_value
    local setup_encode_data = json.encode(setup_data)
    local ok, res = pcall(function()
        self.redfish_import = false
        return self:import_setup_json(ctx, file_type, setup_encode_data, sys_id)
    end)
    if not ok then
        local message = "Set Bios Setup configuration (" .. BIOS_CONFIG_PXE_TIMEOUT_RETRY ..") failed"
        file_service:record_operation(file_service:get_op_initiator(), sys_id, message)
        log:error("[bios] set pxe timeout fail, err is %s", res)
        return msg.SetBiosConfigItemRsp.new(prop_def.BIOS_CONFIG_ITEM_FIELD, manu_id, '')
    end
    local message = "Set Bios Setup configuration (" ..
        BIOS_CONFIG_PXE_TIMEOUT_RETRY ..") to " .. option_value .. " successfully"
    file_service:record_operation(file_service:get_op_initiator(), sys_id, message)
    return msg.SetBiosConfigItemRsp.new(prop_def.BIOS_CONFIG_ITEM_SUCCESS, manu_id)
end

function file_service:get_manu_id()
    -- DB 07 00 -> 00 07 DB
    local manu_id = 0xDB0700
    local bin_id = s_pack("I3", manu_id)
    local res = s_unpack(">I3", bin_id)
    return res
end

function file_service:validate_config_item(req, manu_id)
    if not req.ManufactureId or not req.Length or not req.Item or not req.Option or not manu_id then
        log:error("[bios] validate config item pramas is NULL")
        return msg.SetBiosConfigItemRsp.new(prop_def.BIOS_CONFIG_ITEM_INVALID_FIELD, manu_id, '')
    end

    if req.Length ~= prop_def.SET_BIOS_CONFIG_ITEM_DATA_LEN then
        log:error("[bios] param_len (%d) is invalid, correct length: %d", req.Length,
            prop_def.SET_BIOS_CONFIG_ITEM_DATA_LEN)
        return msg.SetBiosConfigItemRsp.new(prop_def.COMP_CODE_OUTOF_RANGE, manu_id, '')
    end

    if req.Item ~= prop_def.CONFIG_ITEM_FOR_PXE_TIMEOUT_RETRY then
        log:error("[bios] the configuration item (%d) is not supported.",
            prop_def.CONFIG_ITEM_FOR_PXE_TIMEOUT_RETRY)
        return msg.SetBiosConfigItemRsp.new(prop_def.BIOS_CONFIG_ITEM_INVALID_FIELD, manu_id, '')
    end

    if req.ManufactureId ~= manu_id then
        log:error("[bios] validate config item: manufacture_id:%u(ShouldBe:%u) is invalid",
            req.ManufactureId, manu_id)
        return msg.SetBiosConfigItemRsp.new(prop_def.BIOS_CONFIG_ITEM_INVALID_FIELD, manu_id, '')
    end

    return nil
end

function file_service:record_operation(op_initiator, system_id, message)
    local bios_ser = bios_factory.get_service('bios_service')
    if bios_ser and bios_ser:is_multihost() then
        log:system(system_id):operation(op_initiator, 'BIOS', message)
    else
        log:operation(op_initiator, 'BIOS', message)
    end
end

function file_service:get_op_initiator()
    local op_initiator = {}
    local ctx = context.get_context()
    if ctx and ctx.has_initiator and ctx:has_initiator() then
        op_initiator = ctx:get_initiator()
    else
        local mc_initiator = require 'mc.initiator'
        op_initiator = mc_initiator.new('N/A', 'N/A', 'localhost')
    end
    return op_initiator
end

return file_service